Auto reference counting直接翻成中文就是一個物件被別人參考次數。
所以在IOS中,如果一個物件沒有被其他人參考(reference),就會進行記憶體回收,反之,則不會回收記憶體
NOTE:
Reference counting只應用在class的instance. Structure和Enumeration是pass by value,不是pass by reference(reference types),無須擔心記憶體回收問題
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
var reference1: Person? = Person(name: "Kevin")
var reference2: Person? = reference1
reference1 = nil
大家覺得 Person(name: "Kevin") 這個object會被釋放嗎?
答案是不會,因為這個object被reference1和reference2所參考,但是我們只有把reference1設為空,reference2仍有可能被使用,所以ios不會幫我們釋放 Person(name: "Kevin") 這個object,
只有當我們把"reference2 = nil"加上去,才會執行到Person的解構子(deinit)
第二個狀況是: Strong Reference Cycles Between Class Instances
意即兩個class互有對方的reference,範例如下:
class B {
var object: A? = nil
deinit {
print("B deinit :)")
}
}
class A {
var object: B? = nil
deinit {
print("A deinit :)")
}
}
var a: A? = A()
var b: B? = B()
b?.object = a
a?.object = b
a = nil
b = nil
正常情況下,我們會預期當a,b兩個object被設為nil(空)之後,就會執行到class的解構子(deinit),但是因為a和b這兩個object互為對方的資料成員,a要釋放時,發現我還被b參考到.b要釋放時,發現我還被a參考到.所以即使依序把a和b設為空,但記憶體都不會回收
那我們試著把程式碼改成下面這樣:
class B {
var object: A? = nil
deinit {
print("B deinit :)")
}
}
class A {
var object: B? = nil
deinit {
print("A deinit :)")
}
}
var a: A? = A()
var b: B? = B()
b?.object = a
a?.object = b
a?.object = nil
b = nil -------------------> (A)
a = nil -------------------> (B)
我們先釋放a底下的object b,再把a設為空.(A)這行執行完,因為所有持有b的reference都已經清空,所以b能夠正常釋放,接著執行到(B),因為b已經釋放,所以所有持有a的reference也都清空,所以a也能夠正常釋放記憶體
1. Weak reference
2. Unowned reference
1. Weak reference
雖然之前的做法可以正常釋放記憶體,但是當一個class擁有很多個不同型別的資料成員,這樣每次釋放的時候,都要記得先將底下的資料成員設為空,才不會出問題.
但其實有更好的選擇,只要我們在宣告class底下的資料成員 (p.s 要是可以為nil的資料成員) 時,在前面加上weak關鍵字.例如:
class B {
weak var object: A? = nil
deinit {
print("B deinit :)")
}
}
當我們要先釋放a時,即便b object還持有a的reference,ios就會去檢查,原來b和其底下的資料成員和a的關係是弱連結,所以即便那個資料成員還在使用,ios也不會把那個資料成員算入reference count,所以a就能夠正常釋放記憶體.
2. Unowned reference
兩個不同class的instances之間的關係是,一個class的instance life time一定比另一個class的instance的life time來得短或一樣長(即"小於等於"),且比較短instance life time的class持有必定不是nil的另一個class instance
如果看不懂上述解釋的話,舉個簡單的例子,信用卡和人的關係,信用卡的存在時間一定比人的生命短,且信用卡一定有一個持有的人,這時候我們就可以像下面程式碼來定義信用卡和人的class:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer //<-- customer不能是optional(i.e 不可能為nil)
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
ref: https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html